package org.budo.support.lang.util;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.budo.support.java.collection.wrapper.NiceToStringMapWrapper;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.cglib.beans.BeanMap;

import com.google.common.collect.Maps;

/**
 * @author lmw
 */
@SuppressWarnings({ "rawtypes", "unchecked", "cast" })
public class MapUtil {
    private static final Logger log = Slf4j.getLogger();

    public static <K, V> Map<K, V> put(Map<K, V> map, K key, V value) {
        if (null == map) {
            log.error("#25 key=" + key + ", value=" + value + ", map=" + map);
            return newMap(key, value);
        }

        if (null == key || null == value) {
            log.error("#30 key=" + key + ", value=" + value + ", map=" + map);
            return map;
        }

        map.put(key, value);
        return map;
    }

    /**
     * 将Map的Key转为String
     */
    public static <K, V> Map<String, V> stringKeyMap(Map<K, V> map) {
        if (null == map || map.isEmpty()) {
            return (Map<String, V>) map;
        }

        Map result = new HashMap();
        Set<Entry<K, V>> entrySet = map.entrySet();
        for (Entry<K, V> entry : entrySet) {
            K key = entry.getKey();
            if (null == key) {
                continue;
            }

            result.put(key.toString(), entry.getValue());
        }

        return result;
    }

    /**
     * 以两个数组传入key和value，对应组装成map
     */
    public static <K, V> Map<K, V> inject(K[] keys, V[] values) {
        if (null == keys || null == values) {
            return null;
        }

        if (keys.length != values.length) {
            throw new IllegalArgumentException("keys.length=" + keys.length + ", values.length=" + values.length);
        }

        Map<K, V> map = new HashMap<K, V>();
        for (int i = 0; i < keys.length; i++) {
            map.put(keys[i], values[i]);
        }
        return (Map<K, V>) map;
    }

    /**
     * key value 反转
     */
    public static <K, V> Map<V, K> invert(Map<K, V> map) {
        if (null == map) {
            return null;
        }

        Map<V, K> result = new HashMap<V, K>();

        Set<Entry<K, V>> entrySet = map.entrySet();

        for (Entry<K, V> entry : entrySet) {
            K key = entry.getKey();
            V value = entry.getValue();

            if (null != key && null != value) {
                result.put(value, key);
            }
        }
        return result;
    }

    public static Map<String, String> stringMap(String... keyAndValues) {
        Object[] objectKeyAndValues = keyAndValues;
        return newMap(objectKeyAndValues);
    }

    public static Map<String, Object> stringObjectMap(Object... keyAndValues) {
        return newMap(keyAndValues);
    }

    public static Map<String, Integer> stringIntegerMap(Object... keyAndValues) {
        return newMap(keyAndValues);
    }

    public static Map<String, String> stringLinkedHashMap(Object... keyAndValues) {
        return newLinkedHashMap(keyAndValues);
    }

    public static Map<String, Object> stringObjectLinkedHashMap(Object... keyAndValues) {
        return newLinkedHashMap(keyAndValues);
    }

    /**
     * 会新创建一个MAP; 肯定非 NULL
     */
    public static <K, V> Map<K, V> append(Map<K, V> in, Object... keyAndValues) {
        Map<K, V> out = new HashMap<K, V>();

        if (null != in) {
            out.putAll(in);
        }

        Map newMap = newMap(keyAndValues);
        if (null != in) {
            out.putAll(newMap);
        }

        return out;
    }

    /**
     * 合并多个Map
     */
    public static Map merge(Map... maps) {
        Map result = new HashMap();
        if (null == maps || maps.length < 1) {
            return new NiceToStringMapWrapper(result);
        }

        for (Map each : maps) {
            if (null == each) {
                continue;
            }

            result.putAll(each);
        }

        return new NiceToStringMapWrapper(result);
    }

    private static Map newLinkedHashMap(Object[] keyAndValues) {
        return _newMap(keyAndValues, LinkedHashMap.class);
    }

    private static Map<String, Object> _newMap(Object[] keyAndValues, Class<?> type) {
        if (null == keyAndValues) {
            return null;
        }

        int len = keyAndValues.length;
        if (len % 2 != 0) {
            throw new RuntimeException("参数个数应该为偶数 , len=" + len);
        }

        Map map = null;
        if (LinkedHashMap.class.equals(type)) {
            map = new LinkedHashMap();
        } else {
            map = new HashMap();
        }

        for (int i = 0; i < len; i = i + 2) {
            map.put(keyAndValues[i], keyAndValues[i + 1]);
        }

        return map;
    }

    public static Map newMap(Object... keyAndValues) {
        return _newMap(keyAndValues, HashMap.class);
    }

    public static Boolean containsKey(Map map, String key) {
        return null != map && map.containsKey(key);
    }

    public static boolean isNullOrEmpty(Map map) {
        return null == map || map.isEmpty();
    }

    public static <K, V> String toString(Map<K, V> map) {
        if (null == map) {
            return null;
        }

        Iterator<Entry<K, V>> iterator = map.entrySet().iterator();
        if (!iterator.hasNext()) {
            return "{}";
        }

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append('{');
        for (;;) {
            Entry<K, V> entry = iterator.next();

            K key = entry.getKey();
            V value = entry.getValue();
            stringBuilder.append(key == map ? "(this Map)" : key);
            stringBuilder.append('=');

            if (null != value && value.getClass().isArray()) {
                stringBuilder.append(ArrayUtil.toString((Object[]) value));
            } else if (null != value && value instanceof Collection) {
                stringBuilder.append(ArrayUtil.toString(((Collection) value).toArray()));
            } else {
                stringBuilder.append(value == map ? "(this Map)" : value);
            }

            if (!iterator.hasNext()) {
                return stringBuilder.append('}').toString();
            }
            stringBuilder.append(',').append(' ');
        }
    }

    public static <K, V> Map<K, V> select(Map<K, V> map, K... keys) {
        int len = keys.length;
        Map<K, V> result = new HashMap<K, V>();
        for (int i = 0; null != keys && i < len; i++) {
            result.put(keys[i], map.get(keys[i]));
        }
        return result;
    }

    /**
     * 移除Map里的指定的key
     */
    public static <K, V> Map<K, V> remove(Map<K, V> in, K... keys) {
        if (null == in || null == keys) {
            log.error("#146 remove, map=" + in + ", keys=" + Arrays.toString(keys));
            return in;
        }

        Map<K, V> out = new HashMap<K, V>(in);

        for (K key : keys) {
            out.remove(key);
        }

        return out;
    }

    /**
     * 返回以input.value为key，以input.index为value的map
     */
    public static <K> Map<K, Integer> valueToIndexMap(K[] keys) {
        if (null == keys) {
            return null;
        }

        int len = keys.length;
        Map<K, Integer> map = new HashMap<K, Integer>(len);
        for (Integer i = 0; i < len; i++) {
            map.put(keys[i], i);
        }

        return map;
    }

    /**
     * 传入一些Key和共同的Value返回Map
     */
    public static <K, V> Map<K, V> sameValueMap(List<K> keys, V value) {
        if (null == keys) {
            return null;
        }

        int len = keys.size();
        Map<K, V> map = new HashMap<K, V>(len);
        for (Integer i = 0; i < len; i++) {
            map.put(keys.get(i), value);
        }

        return map;
    }

    /**
     * 返回以input.index为key，以input.value为value的map
     */
    public static <K> Map<Integer, K> indexToValueMap(K[] input) {
        if (null == input) {
            return null;
        }

        int len = input.length;
        Map<Integer, K> map = new HashMap<Integer, K>(len);
        for (Integer i = 0; i < len; i++) {
            map.put(i, input[i]);
        }

        return map;
    }

    /**
     * 返回以input.index为key，以input.value为value的map
     */
    public static <K> Map<Integer, K> indexToValueMap(List<K> input) {
        if (null == input) {
            return null;
        }

        return (Map<Integer, K>) indexToValueMap(input.toArray());
    }

    /**
     * 返回以input.value为key，以input.index为value的map
     */
    public static <K> Map<K, Integer> valueToIndexMap(List<K> input) {
        if (null == input) {
            return null;
        }

        return (Map<K, Integer>) valueToIndexMap(input.toArray());
    }

    public static Integer getInteger(Map map, String key, Integer defaultValue) {
        if (null == map || null == key) {
            return null;
        }

        Object value = map.get(key);
        return NumberUtil.toInteger(value, defaultValue);
    }

    public static Integer getInteger(Map map, String key) {
        return getInteger(map, key, null);
    }

    /**
     * 将后面的map中的数据归并到第一个Map，会跳过key或value为空的部分
     */
    public static <K, V> Map<K, V> putAll(Map<K, V> original, Map<?, ?>... append) {
        if (null == original || null == append || append.length < 1) {
            log.warn("#352 putAll, original=" + original + ", append=" + append);
            return original;
        }

        for (Map<?, ?> map : append) {
            if (null == map) {
                log.warn("#358 putAll, original=" + original + ", map=" + map + ", append=" + append);
                continue;
            }

            Set<?> entrySet = map.entrySet();
            for (Object each : entrySet) {
                Entry<?, ?> entry = (Entry<?, ?>) each;
                Object key = entry.getKey();
                Object value = entry.getValue();

                if (null == key || null == value) {
                    log.warn("#369 putAll, key=" + key + ", value=" + value + ", original=" + original + ", map=" + map + ", append=" + append);
                    continue;
                }

                K k = (K) key;
                V v = (V) value;
                original.put(k, v);
            }
        }

        return original;
    }

    /**
     * 返回 key value 交替配对的数组
     */
    public static <T> T[] toPairs(Map<T, T> map) {
        if (null == map) {
            return null;
        }

        List<T> pairs = new ArrayList<T>();
        Set<Entry<T, T>> entrySet = map.entrySet();

        for (Entry<T, T> entry : entrySet) {
            pairs.add(entry.getKey());
            pairs.add(entry.getValue());
        }

        return (T[]) pairs.toArray();
    }

    public static <K, V> V get(Map<K, V> map, K key, V defaultValue) {
        if (null == map || map.isEmpty() || !map.containsKey(key)) {
            return defaultValue;
        }

        return map.get(key);
    }

    public static <K, V> V getFirst(Map<K, V[]> map, K key) {
        return getFirst(map, key, null);
    }

    public static <K, V> V getFirst(Map<K, V[]> map, K key, V defaultValue) {
        if (null == map || map.isEmpty() || !map.containsKey(key)) {
            return defaultValue;
        }

        return ArrayUtil.first(map.get(key));
    }

    public static <K, V> K getKeyByValue(Map<K, V> map, V value) {
        if (null == map || map.isEmpty() || null == value) {
            return null;
        }

        Set<Entry<K, V>> entrySet = map.entrySet();
        for (Entry<K, V> entry : entrySet) {
            V v = entry.getValue();
            if (null != v && v.equals(value)) {
                return entry.getKey();
            }
        }

        return null;
    }

    public static Object getKeyByValue_1(Object map, Object value) {
        if (null == map || null == value) {
            return null;
        }

        if (!(map instanceof Map)) {
            return null;
        }

        Map m = (Map) map;

        Set entrySet = m.entrySet();
        for (Object each : entrySet) {
            Entry entry = (Entry) each;
            Object v = entry.getValue();
            if (null != v && v.equals(value)) {
                return entry.getKey();
            }
        }

        return null;
    }

    /**
     * 将传入的Map的Value转换为int类型
     */
    public static Map<String, Integer> stringIntegerMap(Map<?, ?> map) {
        if (null == map) {
            return null;
        }

        Map<String, Integer> result = new HashMap<String, Integer>();
        Set<?> entrySet = map.entrySet();
        for (Object each : entrySet) {
            Entry entry = (Entry) each;

            Object key = entry.getKey();
            Object value = entry.getValue();

            String key1 = StringUtil.toString(key);
            Integer value1 = NumberUtil.toInteger(value);

            result.put(key1, value1);
        }

        return result;
    }

    public static <K, V> List<V> get(List<Map<K, V>> maps, K key) {
        if (null == maps || null == key) {
            return null;
        }

        List<V> list = new ArrayList<V>();
        for (Map<K, V> map : maps) {
            V value = map.get(key);
            list.add(value);
        }

        return list;
    }

    public static Map<String, String> stringMap(Map<String, ?> map) {
        if (null == map) {
            return null;
        }

        if (map.isEmpty()) {
            return Collections.EMPTY_MAP;
        }

        Map<String, String> stringMap = new HashMap<String, String>();

        Set<?> entrySet = map.entrySet();
        for (Object entry : entrySet) {
            Object _key = ((Entry<?, ?>) entry).getKey();
            String key = (String) _key;
            Object value = ((Entry<?, ?>) entry).getValue();

            if (null == value) {
                stringMap.put(key, null);
                continue;
            }

            if (value.getClass().isArray()) {
                int len = Array.getLength(value);
                if (len < 1) {
                    stringMap.put(key, null);
                    continue;
                }

                if (1 == len) {
                    Object first = Array.get(value, 0);
                    stringMap.put(key, first.toString());
                    continue;
                }

                throw new RuntimeException("#522 entry=" + entry);
            }

            stringMap.put(key, value.toString());
        }

        return stringMap;
    }

    public static <K, V> V firstEntryValue(Map<K, V> map) {
        if (null == map || map.isEmpty()) {
            return null;
        }

        Entry<K, V> next = map.entrySet().iterator().next();
        return next.getValue();
    }

    public static <K, V> Entry<K, V> firstEntry(Map<K, V> map) {
        if (null == map || map.isEmpty()) {
            return null;
        }

        return map.entrySet().iterator().next();
    }

    public static Object firstEntryKey(Object map) {
        if (null == map || !(map instanceof Map<?, ?>)) {
            log.error("#554 firstEntryKey, map=" + map);
            return null;
        }

        Map<?, ?> _map = (Map<?, ?>) map;
        if (_map.isEmpty()) {
            log.warn("#560 _map=" + _map);
            return null;
        }

        return _map.entrySet().iterator().next().getKey();
    }

    public static List<String> stringKeyList(Map<? extends Serializable, ? extends Serializable> map) {
        if (null == map) {
            return null;
        }

        Set<? extends Serializable> keySet = map.keySet();
        return ListUtil.toStringList(keySet.toArray());
    }

    public static List<String> keys(List<Map<String, Object>> maps) {
        if (null == maps) {
            throw new RuntimeException("#74 rows is null");
        }

        Set<String> keys = new HashSet<String>();

        for (Map<String, Object> map : maps) {
            keys.addAll(map.keySet());
        }

        return new ArrayList<String>(keys);
    }

    public static String toString(Map<String, Object> map, String joiner, String linker) {
        if (null == map || map.isEmpty()) {
            log.debug("#597 toString return null, map={}, joiner={}, linker={}", map, joiner, linker);
            return null;
        }

        List<String> list = new ArrayList<String>();

        for (Entry<String, Object> entry : map.entrySet()) {
            list.add(entry.getKey() + joiner + entry.getValue());
        }

        return StringUtil.join(list, linker);
    }

    /**
     * @see #toString(Map, String, String)
     */
    public static String toStringSimply(Map<String, Object> map) {
        if (null == map || map.isEmpty()) {
            log.debug("#615 toStringSimply return null, map={}, joiner={}, linker={}", map);
            return null;
        }

        List<String> list = new ArrayList<String>();

        for (Entry<String, Object> entry : map.entrySet()) {
            Object value = entry.getValue();
            list.add(entry.getKey() + "->" + StringUtil.cutLeft(value + "", 100));
        }

        return StringUtil.join(list, ", ");
    }

    public static Integer size(Map map) {
        return null == map ? 0 : map.size();
    }

    public static <K, V> Map<K, V> sortByKey(Map<K, V> map) {
        if (null == map) {
            return null;
        }

        Comparator<Object> comparator = new Comparator<Object>() {
            public int compare(Object o1, Object o2) {
                return ((Comparable) o1).compareTo(((Comparable) o2));
            }
        };

        Map treeMap = new TreeMap(comparator);
        treeMap.putAll(map);

        return treeMap;
    }

    public static <K, V> Map<K, V> keyUnderscoreToCamel(Map<K, V> map) {
        if (null == map) {
            return null;
        }

        Map _map = new HashMap();
        for (Entry<K, V> entry : map.entrySet()) {
            K key = entry.getKey();
            Object _key = StringUtil.underscoreToCamel((String) key);
            _map.put(_key, entry.getValue());
        }

        return _map;
    }

    /**
     * 跳过 空值 null undefined
     */
    public static <K, V> Map<K, V> skipNullValue(Map<K, V> in) {
        if (null == in || in.isEmpty()) {
            return in;
        }

        Map<K, V> out = new HashMap<K, V>();
        Set<Entry<K, V>> entrySet = in.entrySet();
        for (Entry<K, V> entry : entrySet) {
            V value = entry.getValue();

            if (null == value) {
                continue;
            }

            String string = (value + "").trim();
            if (StringUtils.isBlank(string) || "null".equalsIgnoreCase(string) || "undefined".equalsIgnoreCase(string)) {
                continue;
            }

            K key = entry.getKey();
            out.put(key, value);
        }
        return out;
    }

    public static String getString(Map map, String key, String defaultValue) {
        if (null == map || null == key) {
            return null;
        }

        Object value = map.get(key);
        return null == value ? defaultValue : String.valueOf(value);
    }

    public static String getString(Map map, String key) {
        return getString(map, key, null);
    }

    public static Map<String, Object> beanToMap(Object javaBean) {
        if (null == javaBean) {
            throw new IllegalArgumentException("#1126 MapUtil.beanToMap() javaBean is null.");
        }
        Map<String, Object> resultMap = Maps.newHashMap();
        BeanMap beanMap = BeanMap.create(javaBean);
        if (null != beanMap) {
            for (Object key : beanMap.keySet()) {
                resultMap.put(key + "", beanMap.get(key));
            }
        }
        return resultMap;
    }
}